import static com.lemeshko.xml.BooksParser.getDocument;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.lemeshko.xmlTest.ObjectBinder;
import com.lemeshko.xmlTest.catalog.Authors;
import com.lemeshko.xmlTest.catalog.Book;
import com.lemeshko.xmlTest.catalog.Books;
import com.lemeshko.xmlTest.catalog.Catalog;
import com.lemeshko.xmlTest.catalog.ObjectFactory;

public class ObjectBinderTest {

	private String expectedXMLStr;
	private Catalog testCatalog;
	private Boolean isInit = false;
	private final String OUTPUT_FOLDER = System.getProperty("java.io.tmpdir");

	@Before
	public void init() throws DatatypeConfigurationException,
			ParserConfigurationException, SAXException, IOException {

		if (isInit == false) {
			isInit = true;

			expectedXMLStr = getXMLString("resources/books.xml");

			ObjectFactory factory = new ObjectFactory();

			Books books = factory.createBooks();

			Authors authors = factory.createAuthors();
			authors.getName().add("Jane Doe");

			Book book = factory.createBook();
			book.setAuthors(authors);
			book.setId(111);
			book.setName("Learning JAXB");
			book.setDescription("Step by step instructions for beginners");
			book.setISBN(123445);
			book.setPrice("34 $");

			GregorianCalendar calendar = new GregorianCalendar();
			setCalendarDate(calendar, 2003, Calendar.JANUARY, 1);
			book.setPublishDate(calendar.getTime());

			books.getBook().add(book);

			authors = factory.createAuthors();
			authors.getName().add("John Brown");
			authors.getName().add("Peter T.");

			book = factory.createBook();
			book.setAuthors(authors);
			book.setId(222);
			book.setName("Java Webservices today and Beyond");
			book.setDescription("Information for users so that they can start using Java Web Services Developer Pack");
			book.setISBN(522965);
			book.setPrice("29 $");

			calendar = new GregorianCalendar();
			setCalendarDate(calendar, 2002, Calendar.NOVEMBER, 1);
			book.setPublishDate(calendar.getTime());

			books.getBook().add(book);

			testCatalog = factory.createCatalog();
			testCatalog.setBooks(books);
		}
	}

	@Test
	public void marchalObjectsIntoFileTest() throws JAXBException, IOException,
			SAXException {
		ObjectBinder.marchalObjectsIntoFile(testCatalog, OUTPUT_FOLDER
				+ "/catalog.xml");
		assertEqualXmls(expectedXMLStr, getXMLString(OUTPUT_FOLDER
				+ "/catalog.xml"));
	}

	@Test
	public void marchalObjectsIntoStringTest() throws JAXBException,
			SAXException, IOException {
		String str = ObjectBinder.marchalObjectsIntoString(testCatalog);
		assertEqualXmls(expectedXMLStr, str);
	}

	@Test
	public void unmarshallFromResourceClassPathTest() throws JAXBException {
		Catalog catalog = (Catalog) ObjectBinder
				.unmarshallFromResourceClassPath("books.xml", Catalog.class);

		assertEquals(testCatalog, catalog);
	}

	@Test
	public void unmarshallFromFile() throws JAXBException {
		Catalog catalog = (Catalog) ObjectBinder.unmarshalFromFile(
				"resources/books.xml", Catalog.class);

		assertEquals(testCatalog, catalog);
	}

	@Test
	public void unmarshalFromString() throws JAXBException {
		String res = ObjectBinder.marchalObjectsIntoString(testCatalog);
		Catalog catalog = (Catalog) ObjectBinder.unmarshallFormString(res,
				Catalog.class);

		assertEquals(testCatalog, catalog);
	}

	@Test
	public void calculateCatalogHashCodeTest() throws JAXBException {
		Catalog catalog = (Catalog) ObjectBinder.unmarshalFromFile(
				"resources/books.xml", Catalog.class);

		assertEquals(testCatalog.hashCode(), catalog.hashCode());
	}

	@Test
	public void validateXMLTest() throws ParserConfigurationException,
			SAXException, IOException {
		Document doc = getDocument("resources/books.xml");

		assertNotNull(doc);
	}

	private void setCalendarDate(Calendar calendar, int year, int month, int day) {
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month);
		calendar.set(Calendar.DAY_OF_MONTH, day);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
	}

	private void assertEqualXmls(String expectedXml, String actualXml)
			throws SAXException, IOException {

		XMLUnit.setIgnoreWhitespace(true);
		XMLUnit.setIgnoreAttributeOrder(true);
		XMLUnit.setIgnoreComments(true);

		Diff xmlDiff = new DetailedDiff(new Diff(expectedXml, actualXml));
		xmlDiff.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(
				Integer.MAX_VALUE));
		XMLAssert.assertXMLEqual(
				"The actual XML differs from the expected one", xmlDiff, true);
	}

	private String getXMLString(String url) throws IOException {
		return FileUtils.readFileToString(new File(url));
	}
}
